package boardgamer.yavalath.client.animation;

import android.graphics.Canvas;
import boardgamer.common.client.PeriodicTimer;
import boardgamer.common.client.animation.BlockingAnimation;
import boardgamer.yavalath.client.game.YavalathDrawableBoard;
import boardgamer.yavalath.client.game.YavalathDrawableTile;
import boardgamer.yavalath.common.YavalathTile;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Table;
import com.google.common.collect.Table.Cell;

import java.util.Collections;
import java.util.List;
import java.util.Set;


public class FadingBoardAnimation extends BlockingAnimation {

  private static final Function<Cell<Integer, Integer, YavalathDrawableTile>, YavalathDrawableTile>
      CELL_TO_TILE
      = new Function<Table.Cell<Integer, Integer, YavalathDrawableTile>, YavalathDrawableTile>() {
    @Override
    public YavalathDrawableTile apply(
        Cell<Integer, Integer, YavalathDrawableTile> input) {
      return input.getValue();
    }
  };
  private static final int APPEAR_DELAY = 25;
  private static final int UPDATE_DELAY = 5;
  private static final int ALPHA_DELTA = 4;

  private final boolean isAppearing;

  private final List<YavalathDrawableTile> tilesToFade;
  private final List<YavalathDrawableTile> tilesFading;
  private final PeriodicTimer timer;

  public FadingBoardAnimation(
      final boolean isAppearing,
      Iterable<YavalathDrawableTile> tilesToFade) {
    super(UPDATE_DELAY);
    this.isAppearing = isAppearing;

    this.tilesToFade = Lists.newArrayList(tilesToFade);
    Collections.shuffle(this.tilesToFade);

    this.tilesFading = Lists.newArrayList();

    this.timer = new PeriodicTimer(APPEAR_DELAY) {
      @Override
      protected void onPeriodic() {
        if (!FadingBoardAnimation.this.tilesToFade.isEmpty()) {
          tilesFading.add(FadingBoardAnimation.this.tilesToFade.remove(0));
        }
      }
    };
  }

  public static FadingBoardAnimation forAppearingBoard(
      YavalathDrawableBoard board) {
    return new FadingBoardAnimation(
        true /* isAppearing */,
        Iterables.transform(board.getBoard().cellSet(), CELL_TO_TILE));
  }

  public static FadingBoardAnimation forDisappearingBoard(
      final Predicate<YavalathTile> fadingPredicate,
      YavalathDrawableBoard board) {

    Set<Cell<Integer, Integer, YavalathDrawableTile>> allCells = board.getBoard().cellSet();
    Iterable<YavalathDrawableTile> tilesToFade = Iterables.transform(
        Iterables.filter(allCells,
            new Predicate<Cell<Integer, Integer, YavalathDrawableTile>>() {
              @Override
              public boolean apply(Cell<Integer, Integer, YavalathDrawableTile> input) {
                return fadingPredicate.apply(input.getValue());
              }
            }),
        CELL_TO_TILE);

    return new FadingBoardAnimation(false /* isAppearing */, tilesToFade);
  }

  @Override
  public boolean isDead() {
    return tilesToFade.isEmpty() && Iterables.all(tilesFading,
        new Predicate<YavalathDrawableTile>() {
          @Override
          public boolean apply(YavalathDrawableTile input) {
            if (isAppearing) {
              return input.getAlpha() >= 255;
            } else {
              return input.getAlpha() <= 0;
            }
          }
        });
  }

  @Override
  public void draw(Canvas canvas) { }

  @Override
  protected void onPeriodic() {
    timer.update(UPDATE_DELAY);
    for (YavalathDrawableTile tile : tilesFading) {
      int alpha = tile.getAlpha();
      if (isAppearing) {
        tile.setAlpha(Math.min(255, alpha + ALPHA_DELTA));
      } else {
        tile.setAlpha(Math.max(0, alpha - ALPHA_DELTA));
      }
    }
  }

  @Override
  public boolean isBlocking() {
    return !isDead();
  }
}
